package com.smit.icast;

import android.content.Context;

import com.smit.common.AuthControl;
import com.smit.common.HttpUrl;
import com.smit.dvb.jni.DVB;
import com.smit.dvb.update.IUpdateCallback;
import com.smit.dvb.update.UpdateControl;
import com.smit.network.SyncHttpMannager;
import com.smit.utils.FileUtil;
import com.smit.utils.LogUtil;
import com.smit.utils.MD5Util;
import com.smit.utils.NetUtil;
import com.smit.utils.StrUtil;

import org.apache.http.util.EncodingUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 固件升级
 */
public class FirmwareUpdate implements IUpdateCallback {
    private static final String TAG = "FirmwareUpdate";
    private static final String FIRMWARE_ASSETS_DIR = "icast/firmware/";
    private static final String FIRMWARE_NEW_FILE_NAME = "firmware_new.txt";
    private static final String FIRMWARE_ASSETS_FILE_NAME = "firmware.txt";
    private Context context;
    private String filesDir;
    private final ConcurrentHashMap<String, FirmwareInfo> firmwareInfoMap
            = new ConcurrentHashMap<String, FirmwareInfo>();
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 构造函数
     * @param context Android application context
     */
    public FirmwareUpdate(Context context) {
        this.context = context;
    }
    /**
     * 固件更新消息回调
     *
     * @param msg   消息值
     * @param param 参数
     */
    @Override
    public void handleUpdateCallbackMsg(int msg, Object param) {
        switch (msg) {
            case UpdateControl.MSG_UPDATE_REQUEST_FIRMWARE:
                handleRequest((String) param);
                break;
            default:
                break;
        }
    }

    private void handleRequest(String id1) {
        LogUtil.trace(TAG, "handleRequest start>>>");
        LogUtil.trace(TAG, "handleRequest id=" + id1);
        if (StrUtil.isNullOrEmpty(id1)) {
            LogUtil.error(TAG, "handleRequest id is null!");
            return;
        }
        String id = id1.toLowerCase(Locale.getDefault());
        loadFirmwareData();
        updateFirmwareFromServer(id);
        selectFirmware(id);
        saveFirmwareInfo();
        LogUtil.trace(TAG, "handleRequest end<<<");
    }

    /**
     * 初始化插件配置文件数据 1.读取本地保存的firmware_new.txt 2.从assets中读取firmware.txt
     */
    private synchronized void loadFirmwareData() {
        LogUtil.trace(TAG, "initFirmwareData start>>>");
        filesDir = context.getFilesDir().getAbsolutePath();
        try {
            readWriteLock.readLock().lock();
            File file = new File(filesDir + "/" + FIRMWARE_ASSETS_DIR, FIRMWARE_NEW_FILE_NAME);
            File fileDir = new File(filesDir + "/" + FIRMWARE_ASSETS_DIR);
            if (!fileDir.exists()) {
                fileDir.mkdirs();
            }
            String jsonStr;
            boolean useAssetsFirmware = false;
            if (file.exists()) { //优先读取 files中配置文件
                FileInputStream fileInputStream = new FileInputStream(file);
                int size = fileInputStream.available();
                byte[] buffer = new byte[size];
                fileInputStream.read(buffer);
                fileInputStream.close();
                jsonStr = EncodingUtils.getString(buffer, "UTF-8");
                useAssetsFirmware = false;
            } else { //如果没有就读取assets中文件
                InputStream assets = context.getAssets().open(FIRMWARE_ASSETS_DIR + FIRMWARE_ASSETS_FILE_NAME);
                int length = assets.available();
                byte[] buffer = new byte[length];
                assets.read(buffer);
                jsonStr = EncodingUtils.getString(buffer, "UTF-8");
                useAssetsFirmware = true;
            }
            JSONObject jsonObject = new JSONObject(jsonStr);
            JSONArray jsonArray = jsonObject.getJSONArray("data");
            int len = jsonArray.length();
            for (int i = 0; i < len; i++) {
                JSONObject object = jsonArray.getJSONObject(i);
                FirmwareInfo firmware;
                if (useAssetsFirmware) {
                    firmware = fromAssetsJson(object);
                } else {
                    firmware = fromFileJson(object);
                }
                //LogUtil.debug(TAG, firmware.toString());
                firmwareInfoMap.put(firmware.id.toLowerCase(Locale.getDefault()), firmware);
            }
        } catch (FileNotFoundException e) {
            LogUtil.trace(TAG, "initFirmwareData FileNotFoundException");
        } catch (IOException e) {
            LogUtil.trace(TAG, "initFirmwareData IOException");
        } catch (JSONException e) {
            LogUtil.trace(TAG, "initFirmwareData JSONException");
        } finally {
            readWriteLock.readLock().unlock();
        }
        LogUtil.trace(TAG, "initFirmwareData end<<<");
    }

    /**
     * 从asset文件中的json数据构建 FirmwareInfo，asset为apk默认的
     * @param jsonObject json数据
     * @return 返回FirmwareInfo对象
     */
    FirmwareInfo fromAssetsJson(JSONObject jsonObject) {
        FirmwareInfo firmwareInfo = new FirmwareInfo();
        firmwareInfo.id = jsonObject.optString("id");
        firmwareInfo.fileName = jsonObject.optString("fileName");
        firmwareInfo.assetsVersion = StrUtil.parseInt(jsonObject.optString("version"), 16);
        firmwareInfo.assetsMD5 = jsonObject.optString("md5");
        LogUtil.debug(TAG, "fromAssetsJson" + firmwareInfo.toString());
        return firmwareInfo;
    }

    /**
     * 从firmware_new.txt文件中的json数据构建 FirmwareInfo,firmware_new.txt为程序运行后生成
     * @param jsonObject json数据
     * @return 返回FirmwareInfo对象
     */
    FirmwareInfo fromFileJson(JSONObject jsonObject) {
        FirmwareInfo firmwareInfo = new FirmwareInfo();
        firmwareInfo.id = jsonObject.optString("id");
        firmwareInfo.fileName = jsonObject.optString("fileName");
        firmwareInfo.assetsCopyPath = jsonObject.optString("assets_copy_path");
        firmwareInfo.assetsVersion = jsonObject.optInt("assets_version");
        firmwareInfo.assetsMD5 = jsonObject.optString("assets_md5");

        firmwareInfo.downloadPath = jsonObject.optString("download_path");
        firmwareInfo.downloadVersion = jsonObject.optInt("download_version");
        firmwareInfo.downloadMD5 = jsonObject.optString("download_md5");
        return firmwareInfo;
    }
    
    private synchronized boolean updateFirmwareFromServer(String id) {
        LogUtil.trace(TAG, "updateFirmwareFromServer start>>>");
        if (NetUtil.isNetworkOk(context) == false) {
            LogUtil.error(TAG, "updateFirmwareFromServer isNetworkOk=false");
            return false;
        }
        String apik = AuthControl.getInstance().getApikWait(20000); //这里时间长一点ms
        JSONObject jsonObject;
        SyncHttpMannager httpMannager = new SyncHttpMannager();
        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("apik", apik);

        LogUtil.debug(TAG, "updateFirmwareFromServer apik=" + apik);
        LogUtil.debug(TAG, "updateFirmwareFromServer network=" + NetUtil.isNetworkOk(context));
        if (NetUtil.isNetworkOk(context) && StrUtil.isNullOrEmpty(apik) == false) {
            jsonObject = httpMannager.doSyncGetJson(HttpUrl.getUrl(HttpUrl.DONGLE_FIRMWARE_UPDATE), hashMap);

            if (jsonObject != null) {
                LogUtil.debug(TAG, "updateFirmwareFromServer jsonObject " + jsonObject.toString());
                String result = jsonObject.optString("s");
                if (result != null && result.equalsIgnoreCase("T")) {
                    String downloadUrl = jsonObject.optString("u");
                    String fileMD5 = jsonObject.optString("md5");
                    String sn = jsonObject.optString("v");
                    String versionStr = "0x01";
                    int newVersion = 0;
                    if (!StrUtil.isNullOrEmpty(sn)) {
                        versionStr = sn.replace(id, "0");
                        LogUtil.trace(TAG, "updateFirmwareFromServer versionStr=" + versionStr);
                        newVersion = StrUtil.parseInt(versionStr, 16);
                    }
                    FirmwareInfo dataInfo = firmwareInfoMap.get(id.toLowerCase(Locale.getDefault()));
                    int localVersion = 0;
                    if (dataInfo != null) { //本地有记录
                        int dv = dataInfo.downloadVersion; //下载版本
                        int av = dataInfo.assetsVersion; //内置版本
                        localVersion = dv > av ? dv : av;
                    } else { //本地没有记录的，新的数据，可能是新的地方，有新的营运商
                        dataInfo = new FirmwareInfo();
                        dataInfo.id = id;
                        dataInfo.fileName = id + newVersion;
                        firmwareInfoMap.put(dataInfo.id.toLowerCase(Locale.getDefault()), dataInfo);
                    }
                    LogUtil.debug(TAG, "updateFirmwareFromServer newVersion=" + newVersion
                            + " localVersion=" + localVersion);
                    if (newVersion > localVersion) { //下载文件
                        String downloadPath = filesDir + "/" + FIRMWARE_ASSETS_DIR + dataInfo.fileName + "."
                                + newVersion;
                        File file = new File(downloadPath);
                        if (file.exists()) {
                            file.delete();
                        }
                        boolean ret = downloadFile(downloadUrl, downloadPath);
                        LogUtil.trace(TAG, "updateFirmwareFromServer downloadFile ret=" + ret);
                        if (ret == true) { //检查下载是否正确
                            ret = checkFile(downloadPath, fileMD5);
                            if (ret == true) {
                                File filePrevDownload = new File(dataInfo.downloadPath);
                                if (filePrevDownload.exists()) {
                                    //fileOld.delete(); //这里不能删除，启动的时候已经加载了旧的版本了,保存到asset属性下
                                    File fileAssets = new File(dataInfo.assetsCopyPath); //删除原来的文件。
                                    if (fileAssets.exists()) {
                                        fileAssets.delete();
                                    }
                                    dataInfo.assetsCopyPath = dataInfo.downloadPath;
                                    dataInfo.assetsVersion = dataInfo.downloadVersion;
                                    dataInfo.assetsMD5 = dataInfo.downloadMD5;
                                }
                                dataInfo.downloadMD5 = fileMD5;
                                dataInfo.downloadPath = downloadPath;
                                dataInfo.downloadVersion = newVersion;
                                LogUtil.trace(TAG, "updateFirmwareFromServer downloadFile ok and check Ok");
                                LogUtil.trace(TAG, "updateFirmwareFromServer id=" + id + " end<<<");
                                return true;
                            }
                        }
                    }
                }
            }
        }
        LogUtil.trace(TAG, "updateFirmwareFromServer id=" + id + " end<<<");
        return false;
    }
    /**
     * 检查文件是否存在，并检查文件的md5是否正确，如果没有md5，只检查存在
     *
     * @param filePath 文件全路径
     * @param fileMD5  文件的MD5值，为null就不去检查
     * @return 文件是否正确
     */
    private boolean checkFile(String filePath, String fileMD5) {
        String fileMD5New;
        File libFile;
        LogUtil.trace(TAG, "checkFile start>>>");
        LogUtil.debug(TAG, "checkFile filePath=" + filePath + " fileMD5=" + fileMD5);
        try {
            if (StrUtil.isNullOrEmpty(filePath) == false) {
                libFile = new File(filePath);
                if (libFile.exists()) {
                    fileMD5New = MD5Util.getFileMD5String(libFile);
                    if ((fileMD5New != null && fileMD5New.equalsIgnoreCase(fileMD5))
                            || StrUtil.isNullOrEmpty(fileMD5) == true) { //MD5校验正确  或  没有MD5不需要校验
                        LogUtil.trace(TAG, "checkFile ok");
                        return true;
                    }
                }
            }
        } catch (IOException e) {
            LogUtil.error(TAG, "checkFile IOException");
        }
        LogUtil.error(TAG, "checkFile error");
        return false;
    }
    /**
     * @param url
     * @param saveFilePath
     * @return boolean
     * @Title: downloadFile
     * @Description: 下载文件到指定目录
     */
    private boolean downloadFile(String url, String saveFilePath) {
        boolean ret = false;
        LogUtil.trace(TAG, "downloadFile start>>>");
        LogUtil.debug(TAG, "downloadFile saveFilePath=" + saveFilePath);
        try {
            readWriteLock.writeLock().lock();
            SyncHttpMannager syncHttpMannager = new SyncHttpMannager();
            ret = syncHttpMannager.doSyncDownload(url, saveFilePath);
        } finally {
            readWriteLock.writeLock().unlock();
        }
        return ret;
    }

    private void selectFirmware(String id) {
        LogUtil.trace(TAG, "selectFirmware start>>>");
        if (StrUtil.isNullOrEmpty(id)) {
            LogUtil.error(TAG, "selectFirmware id is null or empty");
        }
        LogUtil.debug(TAG, "selectFirmware id=" + id);
        String sn = "";
        String filePath = "";
        boolean ret = false;
        try {
            FirmwareInfo firmwareInfo = firmwareInfoMap.get(id.toLowerCase(Locale.getDefault()));
            if (firmwareInfo != null) {
                filePath = firmwareInfo.downloadPath; //网络下载的 lib
                LogUtil.debug(TAG, "selectFirmware download_path=" + filePath);
                ret = checkFile(firmwareInfo.downloadPath, firmwareInfo.downloadMD5);
                if (ret) {
                    sn = makeSNStr(id, firmwareInfo.downloadVersion);
                } else {
                    filePath = firmwareInfo.assetsCopyPath; //assets中之前拷贝好的 lib
                    LogUtil.debug(TAG, "selectFirmware assets_copy_path=" + filePath);
                    ret = checkFile(firmwareInfo.assetsCopyPath, firmwareInfo.assetsMD5);
                    if (ret) {
                        sn = makeSNStr(id, firmwareInfo.assetsVersion);
                    } else { // assets中的lib检查不过：1.首次使用，没有拷贝到files 2.文件损坏
                        filePath = filesDir + "/" + FIRMWARE_ASSETS_DIR + firmwareInfo.fileName;
                        File file = new File(filePath);
                        if (file.exists()) {
                            file.delete();
                        }
                        filePath = filesDir + "/" + FIRMWARE_ASSETS_DIR + firmwareInfo.fileName;
                        File fileDir = new File(filesDir + "/" + FIRMWARE_ASSETS_DIR);
                        if (!fileDir.exists()) {
                            fileDir.mkdirs();
                        }
                        FileUtil.copyAssetsFile2Local(context, FIRMWARE_ASSETS_DIR + firmwareInfo.fileName, filePath);
                        ret = checkFile(filePath, firmwareInfo.assetsMD5);
                        if (ret == false) {
                            LogUtil.error(TAG, "selectFirmware " + id + " failed");
                            return;
                        } else {
                            firmwareInfo.assetsCopyPath = filePath;
                            sn = makeSNStr(id, firmwareInfo.assetsVersion);
                            saveFirmwareInfo(); //数据改变了需要保存数据
                            LogUtil.debug(TAG, "selectFirmware copy assets ok and assets_copy_path=" + filePath);
                        }
                    }
                }
            }
        } catch (Exception e) {
            LogUtil.error(TAG, "selectFirmware IOException");
        }
        LogUtil.trace(TAG, "selectFirmware sn=" + sn + " and filePath=" + filePath);
        if (!StrUtil.isNullOrEmpty(sn) && !StrUtil.isNullOrEmpty(filePath)) {
            DVB.getInstance().setFirmwareInfo(sn, filePath);
        }
    }

    private void saveFirmwareInfo() {
        LogUtil.trace(TAG, "saveFirmwareInfo start>>>");
        if (firmwareInfoMap != null && firmwareInfoMap.size() > 0) {
            try {
                readWriteLock.writeLock().lock();
                JSONObject jsonObject = new JSONObject();
                JSONArray jsonArray = new JSONArray();
                for (String key : firmwareInfoMap.keySet()) {
                    FirmwareInfo info = firmwareInfoMap.get(key.toLowerCase(Locale.getDefault()));
                    JSONObject object = info.toJsonObject();
                    jsonArray.put(object);
                }
                jsonObject.putOpt("data", jsonArray);
                FileUtil.writeString2(context, filesDir + "/" + FIRMWARE_ASSETS_DIR
                        + FIRMWARE_NEW_FILE_NAME, jsonObject.toString(4));
            } catch (JSONException e) {
                LogUtil.error(TAG, "saveFirmwareInfo JSONException");
            } catch (Exception e) {
                LogUtil.error(TAG, "saveFirmwareInfo Exception");
            } finally {
                readWriteLock.writeLock().unlock();
                LogUtil.trace(TAG, "saveFirmwareInfo end<<<");
            }
        }
    }

    private String makeSNStr(String id, int version) {
        int len = 24 - id.length();
        String str = String.format(Locale.getDefault(), "0%dx", len);
        str = "%" + str;
        LogUtil.debug(TAG, "makeSNStr str=" + str);
        String sn = id + String.format(Locale.getDefault(), str, version);
        LogUtil.debug(TAG, "makeSNStr sn=" + sn);
        return sn;
    }

    /**
     * 固件信息
     */
    class FirmwareInfo {
        String id;
        String fileName;
        String assetsCopyPath = "";
        int assetsVersion = 0;
        String assetsMD5 = "";
        String downloadPath = "";
        int downloadVersion = 0;
        String downloadMD5 = "";
        /**
         * DVBLibInfo 转json 对象
         * @return json object
         */
        public JSONObject toJsonObject() {
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("id", id);
                jsonObject.put("fileName", fileName);
                jsonObject.put("assets_copy_path", assetsCopyPath);
                jsonObject.put("assets_version", assetsVersion);
                jsonObject.put("assets_md5", assetsMD5);
                jsonObject.put("download_path", downloadPath);
                jsonObject.put("download_version", downloadVersion);
                jsonObject.put("download_md5", downloadMD5);
            } catch (JSONException e) {
                LogUtil.error(TAG, "toJsonObject JSONException");
            }
            return jsonObject;
        }

        /**
         * 输出对象的String
         * @return string 对象
         */
        public String toString() {
            try {
                return toJsonObject().toString(4);
            } catch (JSONException e) {
                LogUtil.error(TAG, "toString JSONException");
            }
            return "";
        }
    }
}